/**
 * 
 */
package org.tp8.ektelesis;

import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JTextField;

import org.jnativehook.keyboard.NativeKeyEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.tp8.ektelesis.exc.KeyHookException;
import org.tp8.ektelesis.exc.SearchToolException;
import org.tp8.ektelesis.listener.CombinationAction;
import org.tp8.ektelesis.model.SearchResult;
import org.tp8.ektelesis.service.intf.HookService;
import org.tp8.ektelesis.service.intf.SearchService;
import org.tp8.ektelesis.swing.SearchResultListModel;

/**
 * @author tpanagopoulos
 * 
 */
@Component
public class Ektelesis extends JFrame implements KeyListener, FocusListener,
		CombinationAction, ActionListener, MouseListener {

	@Value("${ektelesis.size.width}")
	private int width;

	@Value("${ektelesis.size.height}")
	private int height;

	@Value("${ektelesis.index.path}")
	private String path;

	@Value("${ektelesis.index.locations}")
	private String locations;

	private JTextField textField;

	private JList<String> jList;

	@Autowired
	private SearchService searchService;

	@Autowired
	private HookService hookService;

	public void initialize() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setUndecorated(true);
		setSize(width, height);
		setLayout(null);

		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		setLocation((dim.width - width) / 2, (dim.height - height) / 2);

		// Init text field
		textField = new JTextField();
		textField.setSize(width, height);
		textField.addKeyListener(this);
		textField.setLocation(0, 0);
		add(textField);

		// Init JList
		jList = new JList<String>();
		jList.setVisible(false);
		jList.setLocation(textField.getX(),
				textField.getY() + textField.getHeight());
		jList.addMouseListener(this);
		jList.addKeyListener(this);
		add(jList);

		// Init search indexer
		try {
			searchService.openReader(path);
		} catch (SearchToolException e) {
			e.printStackTrace();
		}

		int[] keyCodes = new int[2];
		keyCodes[0] = NativeKeyEvent.VK_WINDOWS;
		keyCodes[1] = NativeKeyEvent.VK_SPACE;
		hookService.applyHook(keyCodes, this);
	}

	public void updateIndex() {
		File file;
		try {
			file = new File(path);
			
			searchService.openWriter(path);

			String[] locationArray = locations.split(",");
			for (String loc : locationArray) {
				file = new File(loc);
				searchService.index(file);
			}

			searchService.closeWriter();
			
		} catch (Exception exc) {
			exc.printStackTrace();
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {

	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
	}

	@Override
	public void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		System.out.println("KeyCode: " + keyCode);
		System.out.println("Keychar: " + e.getKeyChar());
		if (jList.equals(e.getSource()) && (jList.getSelectedIndex() > 0)) {
			if (KeyEvent.VK_ENTER == keyCode) {
				executeSelection();
			} else if ((KeyEvent.VK_UP == keyCode)
					|| (KeyEvent.VK_DOWN == keyCode)) {
				// Do nothing
			} else {
				textField.setText(textField.getText() + e.getKeyChar());
				textField.requestFocus();
			}
			return;
		}

		if (textField.equals(e.getSource())) {
			if (KeyEvent.VK_ENTER == keyCode) {
				executeSelection();
			} else if (KeyEvent.VK_DOWN == keyCode) {
				selectUpDown(true);
			} else if (KeyEvent.VK_UP == keyCode) {
				selectUpDown(false);
			} else {
				String query = textField.getText();
				System.out.println("Query: " + query);
				if (query.trim().length() == 0) {
					return;
				}
				search(query);
			}
			return;
		}
	}

	@Override
	public void focusGained(FocusEvent e) {
		this.setAlwaysOnTop(true);
	}

	@Override
	public void focusLost(FocusEvent e) {
		this.setVisible(false);
		jList.setVisible(false);
		setSize(width, height);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (jList.equals(e.getSource())) {
			executeSelection();
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		if (jList.equals(e.getSource()) && (jList.getSelectedIndex() > 0)) {
			executeSelection();
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void execute() throws KeyHookException {
		setVisible(true);
		requestFocus();
		textField.requestFocus();
	}

	private void executeSelection() {
		SearchResultListModel model = (SearchResultListModel) jList.getModel();
		SearchResult searchResult = model.get(jList.getSelectedIndex());
		File file = new File(searchResult.getPath());
		if (file.exists()) {
			if ((file.isFile()) && (file.canExecute())) {
				Runtime runtime = Runtime.getRuntime();
				try {
					runtime.exec(file.getPath());
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				Desktop desktop = null;
				if (Desktop.isDesktopSupported()) {
					desktop = Desktop.getDesktop();
					try {
						desktop.open(file);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			setVisible(false);
		}
	}

	private void search(String query) {
		List<SearchResult> searchResults;
		try {
			query = query.replaceAll("/", "\\\\/");
			query = query.replaceAll("\\\\", "\\\\");
			query = query + '~';
			
			if (query.startsWith(File.separator)) {
				query = "path:" + query;
			} else {
				query = "name:" + query;
			}
			
			searchResults = searchService.search(query);
		} catch (SearchToolException e) {
			e.printStackTrace();
			searchResults = new ArrayList<SearchResult>();
		}
		showResults(searchResults);
	}

	private void showResults(List<SearchResult> searchResults) {
		SearchResultListModel listModel = new SearchResultListModel(
				searchResults);
		for (SearchResult searchResult : searchResults) {
			System.out.println("Showing result: " + searchResult.getPath());
		}
		jList.setModel(listModel);
		jList.setSize(width, height * listModel.getSize());
		setSize(width, height * (listModel.getSize() + 1));
		jList.setSelectedIndex(0);
		jList.setVisible(true);
		textField.requestFocus();
	}

	private void selectUpDown(boolean increase) {
		int pos = jList.getSelectedIndex();

		if (increase) {
			pos++;
		} else {
			pos--;
		}

		if ((pos < jList.getModel().getSize()) && (pos >= 0)) {
			jList.setSelectedIndex(pos);
		}
	}

}
